package com.ace.lab.starter.annotation;

import com.ace.lab.starter.exception.RpcClassLoadException;
import com.ace.lab.starter.factory.RpcClientFactoryBean;
import io.grpc.stub.AbstractStub;
import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.AbstractBeanDefinition;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.beans.factory.support.GenericBeanDefinition;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.core.type.ClassMetadata;

import java.util.Arrays;
import java.util.Set;

/**
 * 搜寻classpath下的
 */
public class ClassPathRpcClientScanner extends ClassPathBeanDefinitionScanner {

    public ClassPathRpcClientScanner(BeanDefinitionRegistry registry) {
        super(registry);
    }

    public void registerFilters() {
        addIncludeFilter((metadataReader, metadataReaderFactory) -> {
            ClassMetadata classMetadata = metadataReader.getClassMetadata();
            String className = classMetadata.getClassName();
            try {
                Class<?> aClass = Class.forName(className);
                return AbstractStub.class.isAssignableFrom(aClass);
            } catch (ClassNotFoundException e) {
                throw new RpcClassLoadException("load rpc client class " + className + " error.", e);
            }
        });
    }

    @Override
    public Set<BeanDefinitionHolder> doScan(String... basePackages) {
        Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
        if (beanDefinitions.isEmpty()) {
            logger.warn("No rpc client was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
        } else {
            processBeanDefinitions(beanDefinitions);
        }
        return beanDefinitions;
    }

    @Override
    protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
        String className = beanDefinition.getBeanClassName();
        try {
            Class<?> aClass = Class.forName(className);
            return AbstractStub.class.isAssignableFrom(aClass);
        } catch (ClassNotFoundException e) {
            throw new RpcClassLoadException("load rpc client class " + className + " error.", e);
        }
    }

    private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
        GenericBeanDefinition definition;
        for (BeanDefinitionHolder holder : beanDefinitions) {
            definition = (GenericBeanDefinition) holder.getBeanDefinition();
            definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName());
            definition.setBeanClass(RpcClientFactoryBean.class);
            definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
            definition.setScope(BeanDefinition.SCOPE_SINGLETON);
        }
    }
}
